﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace moose.irc
{

    /// <summary>
    /// IRC Channel
    /// </summary>
    /// <remarks>
    /// there is all kinds of 'black magic' happening here with the user management,
    /// might be woth breaking some of this down into an IrcUserCollection class or something,
    /// just to keep the bulk of all of this out of here, 
    /// but that is not the point of this prototype experiment,
    /// will consider it, and anything else you guys wanna suggest..
    /// </remarks>
    [CLSCompliant(true)]
    public class IrcChannel : IrcEntity
    {

        #region [Local Attributes]

        /// <summary>
        /// Collection of channel modes
        /// </summary>
        private Dictionary<char, string> m_dictChannelModes;

        /// <summary>
        /// Collection of users in channel
        /// </summary>
        private IrcUserCollection m_colUsers;

        /// <summary>
        /// Collection of user channel modes, and references to users theirin
        /// </summary>
        private Dictionary<char, List<IrcUser>> m_dictUserModes;

        /// <summary>
        /// Name of channel
        /// </summary>
        private string m_strChannelName;

        /// <summary>
        /// Channel Topic
        /// </summary>
        private string m_strChannelTopic;

        #endregion

        #region [Public Properties]

        /// <summary>
        /// Enumerate users in channel
        /// </summary>
        public IEnumerable<IrcUser> Users
        {
            get
            {
                return m_colUsers.get_all();
            }
        }

        /// <summary>
        /// Gets/Sets Channel Topic
        /// </summary>
        /// <returns>Channel Topic</returns>
        public string Topic
        {
            get
            {
                return m_strChannelTopic;
            }
            set
            {
                m_strChannelTopic = value;
            }
        }

        /// <summary>
        /// Gets Channel Name
        /// </summary>
        /// <returns>Channel Name</returns>
        public string Name
        {
            get
            {
                return m_strChannelName;
            }
        }

        /// <summary>
        /// Get Channel Modes
        /// </summary>
        public Dictionary<char, string> Modes {
            get {
                return m_dictChannelModes;
            }
        }

        #endregion

        #region [Construction]

        /// <summary>
        /// Initialize Irc Channel
        /// </summary>
        /// <param name="channel_name">name of irc channel</param>
        public IrcChannel(string channel_name)
            : base(channel_name)
        {
            m_strChannelName = channel_name;
            initialize_collections();
            return;
        }

        /// <summary>
        /// Initialize All of our collections
        /// </summary>
        private void initialize_collections()
        {
            m_dictChannelModes = new Dictionary<char, string>();
            m_colUsers = new IrcUserCollection();
            m_dictUserModes = new Dictionary<char, List<IrcUser>>();
            return;
        }

        #endregion

        #region [Public Methods]

        /// <summary>
        /// Server Mode Response Parser
        /// </summary>
        /// <param name="mode_response">Server Mode Response</param>
        /// <remarks>
        /// This actually isnt a raw server message parser,
        /// I just want the mode line, like
        /// +abc-sa arg
        /// -m arg
        /// +x arg
        /// 
        /// That type of thing
        /// 
        /// Anyways, this is kinda messy, just here to fill space for now I guess,
        /// to be totally honest, this isn't where Im focusing my efforts right now...
        /// 
        /// Move along, nothing to see here...
        /// </remarks>
        public void update_modes(string mode_response)
        {
            bool add = false;
            char c = (char)0x0;
            mode_response = mode_response.Trim();
            for (int i = 0; i < mode_response.Length; i++)
            {
                if (c == '+')
                {
                    add = true;
                    continue;
                }
                else if (c == '-')
                {
                    add = false;
                    continue;
                }
                if (add)
                {
                    if (i < mode_response.Length - 1)
                    { //Test if there are any arguments for this item
                        if (mode_response[i + 1] == ' ')
                        { //There is an argument
                            string arg = mode_response.Substring(i).Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1];
                            if (arg == "*")
                            {
                                m_dictChannelModes[c] = string.Empty;
                            }
                            else
                            {
                                m_dictChannelModes[c] = arg;
                            }
                        }
                    }
                    else
                    { //If this is the last character, then there are no arguments
                        m_dictChannelModes[c] = string.Empty;
                    }
                }
                else
                {
                    m_dictChannelModes.Remove(c);
                }
            }
            return;
        }

        /// <summary>
        /// Add User to channel, or user joins channel
        /// </summary>
        /// <param name="user_string">user string</param>
        /// <remarks>
        /// There are a few ways the user may be joining the channel
        /// 
        /// There is by raw 353, for when users are already in the channel when you join,
        /// in which case, we will have a nickname, and possibly a symbol, @ for Op and + for voice
        /// 
        /// there is also by the JOIN server EVENT/Response,
        /// 
        /// and in that case, we will actually have the full BNF user name
        /// nick!user@host, and in those cases, they will not have a channel mode yet.  We will get an
        /// event indicating any promotions for this...
        /// 
        /// </remarks>
        public void add_user(string user_string)
        {
            char mode = user_string[0];
            if (mode == '@' || mode == '+')
            {
                user_string = user_string.Substring(1); //Cut the symbol off of the user
                update_user_mode(m_colUsers.get_user(user_string), "+" + mode);
            } else {
                m_colUsers.get_user(user_string);
            }
            return;
        }

        /// <summary>
        /// Remove User from channel, by part, disconnect, kick or whatever
        /// </summary>
        /// <param name="user_string">user string of user removed</param>
        /// <remarks>
        /// I have a little bit of investigation to see what this will actually look like,
        /// havent looked at each of the messages yet, to see how they will look,
        /// nor have I seen all situations where a user may leave a channel.
        /// </remarks>
        public void remove_user(string user_string)
        {
            IrcUser user = m_colUsers.get_user(user_string);
            foreach (char mode in m_dictUserModes.Keys)
            {
                if (m_dictUserModes[mode].Contains(user))
                    m_dictUserModes[mode].Remove(user);
            }
            m_colUsers.remove_user(user.Nickname);
            return;
        }

        /// <summary>
        /// Update users mode
        /// </summary>
        /// <param name="user">user to modify mode for</param>
        /// <param name="mode_line"> mode line, will be +o, +v or -o or -v or whatever</param>
        /// <remarks>
        /// Yes, I realize this is super messy, I'm just working within the lines I set myslef up for the other day..
        /// Since these come in as mode lines, I wonder if I'm taking entirely the wrong approach here...
        /// </remarks>
        public void update_user_mode(IrcUser user, string mode_line)
        {
            bool bAdd = mode_line[0]== '+';
            if (!m_dictUserModes.ContainsKey(mode_line[1]))
                m_dictUserModes[mode_line[1]] = new List<IrcUser>();
            if (bAdd)
                m_dictUserModes[mode_line[1]].Add(user);
            else
                m_dictUserModes[mode_line[1]].Remove(user);
            return;
        }

        #endregion

    }
}
